<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 对数组元素或属性的读取操作
        // 1. 通过索引访问数组元素值: arr[0]
        // 2. 访问数组的长度: arr.length
        // 3. 把数组作为对象, 使用for...in循环遍历
        // 4. 使用for...of迭代遍历数组
        // 5. 数组的原型方法,如concat/join/every/some/find/findIndex/includes等

        // 对数组元素或属性的设置操作
        // 1. 通过索引修改数组元素值: arr[1] = 3
        // 2. 修改数组长度: arr.length = 0
        // 3. 数组的栈方法: push/pop/shift/unshift
        // 4. 修改原数组的原型方法: splice/fill/sort


        // const originMethod = Array.prototype.includes
        // const arrayInstrumentations = {
        //     inclueds: function (...args) {
        //         let res = originMethod.apply(this, args)
        //         if (res === false) {
        //             res = originMethod.apply(this.raw, args)
        //         }
        //         return res
        //     },
        //     indexOf: function () {

        //     },
        //     lastIndexOf: function() {

        //     }
        // }

        // 简写为下面的写法
        const arrayInstrumentations = {}
            ;['inclueds', 'indexOf', 'lastIndexOf'].forEach(method => {
                const originMethod = Array.prototype[method]
                arrayInstrumentations[method] = function (...args) {
                    let res = originMethod.apply(this, args)
                    if (res === false) {
                        res = originMethod.apply(this.raw, args)
                    }
                    return res
                }
            })

        // 重写push方法
        let shouldTrack = true
            ;['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
                const originMethod = Array.prototype[method]
                arrayInstrumentations[method] = function (...args) {
                    // 在调用原始方法之前, 禁止追踪
                    shouldTrack = false
                    let res = originMethod.apply(this, args)
                    // 在调用原始方法之后, 恢复原来的行为, 即允许追踪
                    shouldTrack = true
                    return res
                }
            })

        function createRactive(obj, isShallow = false, isReadOnly = false) {
            return new Proxy(obj, {
                get(target, key, receiver) {
                    if (key === 'raw') {
                        return target
                    }

                    if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
                        return Reflect.get(arrayInstrumentations, key, receiver)
                    }
                    // 添加判断, 如果key的类型是symbol,则不进行追踪
                    if (!isReadOnly && typeof key !== 'symbol') {
                        track(target, key)
                    }
                    const res = Reflect.get(target, key, receiver)
                    if (isShallow) return res
                    if (typeof res === 'object' && res !== null) {
                        return isReadOnly ? readOnly(res) : reactive(res)
                    }
                    return res
                },
                set(target, key, newVal, receiver) {
                    if (isReadOnly) {
                        console.warn(`属性{key}是只读的`)
                        return true
                    }
                    const oldVal = target[key]
                    const type = Array.isArray(target)
                        ? Number(key) < target.length ? 'SET' : 'ADD'
                        : Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'

                    const res = Reflect.set(target, key, newVal, receiver)
                    if (target === receiver.raw) {
                        if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
                            trigger(target, key, type, newVal)
                        }
                    }

                    return res
                },
                ownKeys(target) {
                    track(target, Array.isArray(target) ? 'length' : ITERATE_KEY)
                    return Reflect.ownKeys(target)
                }
            })
        }

        function trigger(target, key, type, newVal) {
            const desMap = bucket.get(target)
            if (!desMap) return
            // 省略部分内容

            if (type === 'ADD' && Array.isArray(target)) {
                const lengthEffects = desMap.get('length')
                lengthEffects && lengthEffects.forEach(effectFn => {
                    if (effectFn !== activeEffect) {
                        effectToRun.add(effectFn)
                    }
                })
            }

            if (Array.isArray(target) && key === 'length') {
                depsMap.forEach((effects, key) => {
                    // 数组的key即index
                    if (key >= newVal) {
                        effects.forEach(effectFn => {
                            if (effectFn !== activeEffect) {
                                effectToRun.add(effectFn)
                            }
                        })
                    }
                })
            }

            effectToRun.forEach(effectFn => {
                if (effectFn.options.scheduler) {
                    effectFn.options.scheduler(effectFn)
                } else {
                    effectFn()
                }
            })
        }


        const reactiveMap = new Map()
        function reactive(obj) {
            // 优先通过原始对象obj寻找之前创建的代理对象,如果找到了,直接返回已有的代理对象
            const existionProxy = reactiveMap.get(obj)
            if (existionProxy) return existionProxy
            // 否则创建新的代理对象 
            const proxy = createRactive(obj)
            reactiveMap.set(obj, proxy)

            return proxy
        }

    </script>
</body>

</html>